//Modified from OpenGL tutorial at http://in2gpu.com/
//Thanks to their awesome tutorial!!!

#include "Shader_Loader.h"

using namespace Core;
 
Shader_Loader::Shader_Loader(void){}
Shader_Loader::~Shader_Loader(void){}
 
std::string Shader_Loader::ReadShader(char *filename)
{
	std::string shaderCode;
	std::ifstream file(filename, std::ios::in);
 
	if(!file.good())
	{
		std::cout<<"Can't read file "<<filename<<std::endl;
		std::terminate();
	}
 
	file.seekg(0, std::ios::end);
	shaderCode.resize((unsigned int)file.tellg());
	file.seekg(0, std::ios::beg);
	file.read(&shaderCode[0], shaderCode.size());
	file.close();
	return shaderCode;
}
 
GLuint Shader_Loader::CreateShader(GLenum shaderType, std::string source, char* shaderName)
{
 
	int compile_result = 0;
 
	GLuint shader = glCreateShader(shaderType);
	const char *shader_code_ptr = source.c_str();
	const int shader_code_size = source.size();
 
	glShaderSource(shader, 1, &shader_code_ptr, &shader_code_size);
	glCompileShader(shader);
	glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_result);
 
	//check for errors
	if (compile_result == GL_FALSE)
	{
 
		int info_log_length = 0;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);
		std::vector<char> shader_log(info_log_length);
		glGetShaderInfoLog(shader, info_log_length, NULL, &shader_log[0]);
		std::cout << "ERROR compiling shader: " << shaderName << std::endl << &shader_log[0] << std::endl;
		return 0;
	}
	return shader;
}
 
GLuint Shader_Loader::CreateProgram(char* vertexShaderFilename, char* fragmentShaderFilename)
{
	//read the shader files and save the code
	std::string vertex_shader_code = ReadShader(vertexShaderFilename);
	std::string fragment_shader_code = ReadShader(fragmentShaderFilename);
 
	GLuint vertex_shader = CreateShader(GL_VERTEX_SHADER, vertex_shader_code, "vertex shader");
	GLuint fragment_shader = CreateShader(GL_FRAGMENT_SHADER, fragment_shader_code, "fragment shader");
 
	int link_result = 0;
	//create the program handle, attatch the shaders and link it
	GLuint program = glCreateProgram();
	glAttachShader(program, vertex_shader);
	glAttachShader(program, fragment_shader);
 
	glLinkProgram(program);
	glGetProgramiv(program, GL_LINK_STATUS, &link_result);
	//check for link errors
	if (link_result == GL_FALSE)
	{
		int info_log_length = 0;
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);
		std::vector<char> program_log(info_log_length);
		glGetProgramInfoLog(program, info_log_length, NULL, &program_log[0]);
		std::cout << "Shader Loader : LINK ERROR" << std::endl << &program_log[0] << std::endl;
		return 0;
	}

	return program;
}


ProgramData Shader_Loader::loadProgram (char* vertexShaderFilename, char* fragmentShaderFilename, int projectionBlockIndex)
{
	ProgramData data;
	data.theProgram = CreateProgram(vertexShaderFilename, fragmentShaderFilename);
	data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");
	//data.perspectiveMatrixUnif = glGetUniformLocation(data.theProgram, "perspectiveMatrix");
	//data.lightIntensityUnif = glGetUniformLocation(data.theProgram, "lightIntensity");
	//data.ambientIntensityUnif = glGetUniformLocation(data.theProgram, "ambientIntensity");

	//data.normalModelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "normalModelToCameraMatrix");
	//data.cameraSpaceLightPosUnif = glGetUniformLocation(data.theProgram, "cameraSpaceLightPos");
	//data.lightAttenuationUnif = glGetUniformLocation(data.theProgram, "lightAttenuation");
	//data.shininessFactorUnif = glGetUniformLocation(data.theProgram, "shininessFactor");
	//data.baseDiffuseColorUnif = glGetUniformLocation(data.theProgram, "baseDiffuseColor");

	GLuint projectionBlock = glGetUniformBlockIndex(data.theProgram, "projection");
	glUniformBlockBinding(data.theProgram, projectionBlock, projectionBlockIndex);

	return data;

}
